package br.com.hs.nfe.common.util;

import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.apache.xmlbeans.XmlCalendar;

/**
 * Classe responsável por efetuar operações com a String do XML.
 * @author Ranlive Hrysyk
 */
public class XMLHelper {

    private static final String NOME_TAG = "#NOME_TAG#";
    private static final String REGEX = "(<\\s*[/]{0,1}\\s*" + NOME_TAG + "(\\s+[^<]*|\\s*)>|<\\s*" + NOME_TAG + "(\\s+[^<]*|\\s*)/{0,1}\\s*>)";
    private static final String MATCHES = "<[^><]*/\\s*>";

    /**
     * Obtém o conteúdo de uma TAG XML.
     * @param xml String do XML.
     * @param nomeTag Nome da Tag.
     * @param incluirTag Incluir a Tag no conteúdo.
     * @param decodeSpecialChars Substituir caracteres especiais.
     * @return Lista com os valores encontrados.
     */
    public static List<String> getTagConteudo(String xml, String nomeTag, boolean incluirTag, boolean decodeSpecialChars) {
        if ((xml == null) || (nomeTag == null)) {
            return null;
        }
        List tags = new ArrayList();
        String regex = REGEX.replace(NOME_TAG, nomeTag);
        Matcher matcher = Pattern.compile(regex).matcher(xml);
        int start = 0;
        int end = 0;
        int diff = incluirTag ? 1 : 0;
        String group = null;

        while (matcher.find(start)) {
            if (incluirTag) {
                start = matcher.start();
            } else {
                start = matcher.end();
            }

            group = matcher.group();
            if (Pattern.matches(MATCHES, group)) {
                if (incluirTag) {
                    tags.add(decodeSpecialChars ? StringHelper.decodeSpecialXMLChars(group) : group);
                    start += diff;
                    continue;
                }
            }

            matcher.find(start + diff);

            if (incluirTag) {
                end = matcher.end();
            } else {
                end = matcher.start();
            }

            tags.add(decodeSpecialChars ? StringHelper.decodeSpecialXMLChars(xml.substring(start, end)) : xml.substring(start, end));
            start = matcher.end();
        }

        return tags.isEmpty() ? null : tags;
    }

    /**
     * 
     * Obtém o conteúdo de uma TAG XML.
     * @param xml String do XML.
     * @param nomeTag Nome da Tag.
     * @param incluirTag Incluir a Tag no conteúdo.
     * @return Lista com os valores encontrados.
     */
    public static List<String> getTagConteudo(String xml, String nomeTag, boolean incluirTag) {
        return getTagConteudo(xml, nomeTag, incluirTag, false);
    }

    /**
     * Obtém o primeiro valor de uma Tag.
     * @param xml String do XML.
     * @param nomeTag Nome da Tag.
     * @param incluirTag Incluir a Tag no conteúdo.
     * @param decodeSpecialChars Substituir caracteres especiais.
     * @return String com o conteúdo.
     */
    public static String getFirstTagConteudo(String xml, String nomeTag, boolean incluirTag, boolean decodeSpecialChars) {
        List list = getTagConteudo(xml, nomeTag, incluirTag, decodeSpecialChars);
        if ((list != null) && (!list.isEmpty())) {
            return (String) list.get(0);
        }
        return null;
    }

    /**
     * Altera o valor de uma Tag.
     * @param xml String do XML.
     * @param tagName Nome da Tag.
     * @param newValue Novo valor.
     * @return String do XML com o novo valor.
     */
    public static String alterarTagConteudo(String xml, String tagName, String newValue) {
        Matcher matcher = Pattern.compile("(<\\s*" + tagName + "(?:\\s[^<]*)?)(?:/\\s*)>").matcher(xml);
        if (matcher.find()) {
            return xml.substring(0, matcher.start()) + matcher.group(1) + ">" + newValue + "</" + tagName + ">" + xml.substring(matcher.end(), xml.length());
        }
        matcher = Pattern.compile("(<\\s*" + tagName + "(?:\\s[^<]*)?>)(?:[^<]*)(?:</" + tagName + "\\s*>)").matcher(xml);
        if (matcher.find()) {
            return xml.substring(0, matcher.start()) + matcher.group(1) + newValue + "</" + tagName + ">" + xml.substring(matcher.end(), xml.length());
        }
        return xml;
    }

    /**
     * Altera o valor de um atributo do XML.
     * @param xml String do XML.
     * @param tagName Nome da Tag.
     * @param attribName Nome do Atributo.
     * @param newValue Novo Valor.
     * @return String do XML com o novo valor.
     */
    public static String alterarAtributoTag(String xml, String tagName, String attribName, String newValue) {
        Matcher matcher = Pattern.compile("<\\s*" + tagName + "(?:[^<]*)?(\\s" + attribName + "\\s*=\\s*\"[^<]*\")(?:\\s[^<]*)?(?:/?\\s*)>").matcher(xml);
        if (matcher.find()) {
            int beginAttrib = matcher.group(0).indexOf(matcher.group(1));
            int endAttrib = matcher.group(0).lastIndexOf("\"");
            String newAttrib = matcher.group(0).substring(0, beginAttrib) + " " + attribName + "=\"" + newValue + "\"" + matcher.group(0).substring(endAttrib + 1, matcher.group(0).length());
            return xml.substring(0, matcher.start()) + newAttrib + xml.substring(matcher.end(), xml.length());
        }
        return xml;
    }

    /**
     * Obtém uma data de uma String de data XML.
     * @param xmlDate Data String.
     * @return Date convertido.
     */
    public static Date getXmlDate(String xmlDate) {
        return new XmlCalendar(xmlDate).getTime();
    }

    /**
     * Obtém um Calendar de uma String de data do XML.
     * @param xmlDate Data String.
     * @return Calendar convertido.
     */
    public static Calendar getXmlCalendar(String xmlDate) {
        return new XmlCalendar(xmlDate);
    }
}
